/** * Copyright (c) 2002-2011 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.smack.routing; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.HEAD; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import org.neo4j.smack.pipeline.database.event.Invocation; import org.neo4j.smack.pipeline.database.event.Output; import org.neo4j.smack.routing.annotation.DeserializeWith; import org.neo4j.smack.routing.annotation.SerializeWith; import org.neo4j.smack.routing.annotation.Transactional; import org.neo4j.smack.serialization.DeserializationStrategy; import org.neo4j.smack.serialization.SerializationStrategy; public class AnnotationBasedRoutingDefinition extends RoutingDefinition { private final Object underlyingObject; private class MethodInvokingEndpoint implements Endpoint { private final Object underlyingObject; private final Method method; private final InvocationVerb verb; private final boolean isTransactional; private final SerializationStrategy<?> serializationStrategy; private final DeserializationStrategy<?> deserializationStrategy; public MethodInvokingEndpoint(InvocationVerb verb, Method method, boolean isTransactional, Object underlyingObject, SerializationStrategy<?> serializationStrategy, DeserializationStrategy<?> deserializationStrategy) { this.verb = verb; this.method = method; this.isTransactional = isTransactional; this.underlyingObject = underlyingObject; this.serializationStrategy = serializationStrategy; this.deserializationStrategy = deserializationStrategy; } public void invoke(Invocation request, Output result) throws Exception { try { method.invoke(underlyingObject, request, result); } catch(InvocationTargetException e) { if(e.getCause() != null && e.getCause() instanceof Exception) { throw (Exception)e.getCause(); } throw e; } } @Override public String toString() { return String.format("MethodInvokingEndpoint{method=%s, verb=%s}", method, verb); } @Override public InvocationVerb getVerb() { return verb; } @Override public boolean isTransactional() { return isTransactional; } @Override public DeserializationStrategy<?> getDeserializationStrategy() { return deserializationStrategy; } @Override public SerializationStrategy<?> getSerializationStrategy() { return serializationStrategy; } } public AnnotationBasedRoutingDefinition(Object obj) { this.underlyingObject = obj; setupRoutes(); } private void setupRoutes() { for (Method m : underlyingObject.getClass().getMethods()) { if (m.isAnnotationPresent(GET.class)) { addRoute(m, InvocationVerb.GET); } if (m.isAnnotationPresent(PUT.class)) { addRoute(m, InvocationVerb.PUT); } if (m.isAnnotationPresent(POST.class)) { addRoute(m, InvocationVerb.POST); } if (m.isAnnotationPresent(DELETE.class)) { addRoute(m, InvocationVerb.DELETE); } if (m.isAnnotationPresent(HEAD.class)) { addRoute(m, InvocationVerb.HEAD); } } } private void addRoute(final Method method, final InvocationVerb verb) { String path = ""; boolean isTransactional = false; SerializationStrategy<?> serializationStrategy = SerializationStrategy.NO_OP; DeserializationStrategy<?> deserializationStrategy = DeserializationStrategy.NO_OP; try { method.setAccessible(true); if (method.isAnnotationPresent(Path.class)) { path = method.getAnnotation(Path.class).value(); } if (method.isAnnotationPresent(SerializeWith.class)) { serializationStrategy = (SerializationStrategy<?>) method .getAnnotation(SerializeWith.class).value() .getConstructor().newInstance(); } if (method.isAnnotationPresent(DeserializeWith.class)) { deserializationStrategy = (DeserializationStrategy<?>) method .getAnnotation(DeserializeWith.class).value() .getConstructor().newInstance(); } if (method.isAnnotationPresent(Transactional.class)) { isTransactional = true; } Endpoint endpoint = new MethodInvokingEndpoint(verb, method, isTransactional, underlyingObject, serializationStrategy, deserializationStrategy); if (path.isEmpty() || path.startsWith("/")) { addRoute(path, endpoint); } else { addRoute("/" + path, endpoint); } } catch (NoSuchMethodException e) { throw new RuntimeException( "Serialization/deserialization strategies must implement no-arg constructor.", e); } catch (Exception e) { throw new RuntimeException( "Unable to create service from annotated class for path '" + path + "'.", e); } } }